package lpg;

import java.util.List;

class Action
{
    Action(Control control, Blocks actionBlocks, Grammar grammar, LookupTable<String, MacroSymbol> macroTable)
    {
        this.control        = control;
        this.actionBlocks   = actionBlocks;
        this.grammar        = grammar;
        this.option         = control.option;
        this.lexStream      = control.lexStream;
        this.macroTable     = macroTable;

        abstractAstListClassname = "Abstract" + option.astType + "List";
    }

    int localMacroTableSize()
    {
        return localMacroTable.size();
    }

    SimpleMacroSymbol getLocalMacro(int i)
    {
        return localMacroTable.element(i);
    }

    int filterMacroTableSize()
    {
        return filterMacroTable.size();
    }

    SimpleMacroSymbol getFilterMacro(int i)
    {
        return filterMacroTable.element(i);
    }

    SimpleMacroSymbol insertLocalMacro(String str)
    {
        return insertLocalMacro(str, null);
    }

    SimpleMacroSymbol insertLocalMacro(String str, String value)
    {
        String macroName = option.escape + str;
        SimpleMacroSymbol macroSymbol = new SimpleMacroSymbol(macroName, localMacroTable.size());
        localMacroTable.insert(macroName, macroSymbol);

        if (value != null)
            macroSymbol.setValue(value);

        return macroSymbol;
    }

    SimpleMacroSymbol insertLocalMacro(String macroName, int value)
    {
        return insertLocalMacro(macroName, String.valueOf(value));
    }

    SimpleMacroSymbol insertExportMacro(int exportToken)
    {
        String macroName = option.escape + '_' + lexStream.nameString(exportToken);

        SimpleMacroSymbol macroSymbol = exportMacroTable.findOrInsert(
                macroName, new SimpleMacroSymbol(macroName, exportMacroTable.size()));
        macroSymbol.setLocation(lexStream.getTokenReference(exportToken));

        return macroSymbol;
    }

    SimpleMacroSymbol insertFilterMacro(String name, String value)
    {
        String macroName = option.escape + name;

        SimpleMacroSymbol macroSymbol = filterMacroTable.findOrInsert(
                macroName, new SimpleMacroSymbol(macroName, filterMacroTable.size()));
        macroSymbol.setValue(value);

        return macroSymbol;
    }

    SimpleMacroSymbol insertRuleMacro(String macroName, String value)
    {
        SimpleMacroSymbol macroSymbol = new SimpleMacroSymbol(macroName, ruleMacroTable.size());
        ruleMacroTable.insert(macroName, macroSymbol);
        assert (value != null);
        macroSymbol.setValue(value);

        return macroSymbol;
    }

    SimpleMacroSymbol insertRuleMacro(String macroName, int value)
    {
        return insertRuleMacro(macroName, String.valueOf(value));
    }

    SimpleMacroSymbol insertUndeclaredMacro(String macroName)
    {
        SimpleMacroSymbol macroSymbol = new SimpleMacroSymbol(macroName, undeclaredMacroTable.size());
        undeclaredMacroTable.insert(macroName, macroSymbol);
        return macroSymbol;
    }

    SimpleMacroSymbol findLocalMacro(String str)
    {
        return localMacroTable.find(str.toLowerCase());
    }

    SimpleMacroSymbol findRuleMacro(String str)
    {
        return ruleMacroTable.find(str);
    }

    SimpleMacroSymbol findFilterMacro(String str)
    {
        return filterMacroTable.find(str);
    }

    SimpleMacroSymbol findExportMacro(String str)
    {
        return exportMacroTable.find(str);
    }

    MacroSymbol findUserDefinedMacro(String str)
    {
        return macroTable.find(str);
    }

    SimpleMacroSymbol findUndeclaredMacro(String str)
    {
        return undeclaredMacroTable.find(str);
    }

    void insertExportMacros()
    {
        firstLocallyExportedMacro = exportMacroTable.size();

        for (int i = 0, n = grammar.parser.exports.size(); i < n; i++)
        {
            int exportToken = grammar.parser.exports.get(i);
            SimpleMacroSymbol macro = insertExportMacro(exportToken);
            if (findLocalMacro(macro.name()) != null)
            {
                option.emitError(exportToken,
                        "The name of the exported symbol " + macro.name() +
                        " conflicts with the predefined macro of the same name");

                errorFlag = true;
            }
        }

        locallyExportedMacroGate = exportMacroTable.size();
    }

    //
    // Make sure that all terminals exported locally in this file were also generated locally.
    //
    void checkExportMacros()
    {
        for (int i = firstLocallyExportedMacro; i < locallyExportedMacroGate; i++)
        {
            SimpleMacroSymbol simpleMacro = exportMacroTable.element(i);
            if (!simpleMacro.isUsed())
            {
                option.emitWarning(simpleMacro.location(),
                        "The exported terminal symbol " + simpleMacro.name().substring(2) + //Phileas TODO: why 2?
                        " was not generated by any rule in this grammar");
            }
        }
    }


    void insertImportedFilterMacros()
    {
        for (int i = 0, n = lexStream.numImportedFilters(); i < n; i++)
        {
            int exportToken = lexStream.importedFilter(i);
            SimpleMacroSymbol macro = insertExportMacro(exportToken);
            if (findLocalMacro(macro.name()) != null)
            {
                option.emitError(exportToken,
                        "The name of the exported symbol " + macro.name() +
                        " conflicts with the predefined macro of the same name");

                errorFlag = true;
            }
        }
    }


    //
    // We now make sure that none of the user-defined macros appear
    // in either the local macro table or the export_macro_table.
    //
    void checkMacrosForConsistency()
    {
        for (int i = 0, n = macroTable.size(); i < n; i++)
        {
            MacroSymbol macro = macroTable.element(i);
            if (findLocalMacro(macro.name()) != null)
            {
                option.emitError(macro.location(),
                        "The user-defined macro " + macro.name() +
                        " conflicts with the predefined macro of the same name");

                errorFlag = true;
            }
            else if (findExportMacro(macro.name()) != null)
            {
                option.emitError(macro.location(),
                        "The user-defined macro " + macro.name() +
                        " conflicts with an exported symbol of the same name");

                errorFlag = true;
            }
        }
    }

    void setupBuiltinMacros()
    {
        //TODO
    }

    void processCodeActions(List<ActionBlockElement> headerActions,
            String[] typestring, List<List<ProcessedRuleElement>> processedRuleMap)
    {
        //TODO
    }

    String getDefaultTerminalType()
    {
        return null; //TODO
    }

    String getDefaultNonterminalType()
    {
        return null; //TODO
    }

    void generateDefaultTitle(List<ActionBlockElement> noticeActions)
    {
        //TODO
    }

    void processAstActions(List<ActionBlockElement> actions,
            List<ActionBlockElement> noticeActions, List<ActionBlockElement> initialActions,
            String[] typestring, List<List<ProcessedRuleElement>> processedRuleMap,
            LookupTable<String, Symbol> classnameSet, List<ClassnameElement> classname)
    {
        //TODO
    }

    boolean errorFlag = false;

    protected final Control control;
    protected final Blocks actionBlocks;
    protected final Grammar grammar;
    protected final Option option;
    protected final LexStream lexStream;

    protected final String abstractAstListClassname;
    protected int firstLocallyExportedMacro = 0;
    protected int locallyExportedMacroGate = 0;

    protected final LookupTable<String, MacroSymbol> macroTable;

    protected final LookupTable<String, SimpleMacroSymbol> localMacroTable = new LookupTable<String, SimpleMacroSymbol>();
    protected final LookupTable<String, SimpleMacroSymbol> ruleMacroTable = new LookupTable<String, SimpleMacroSymbol>();
    protected final LookupTable<String, SimpleMacroSymbol> filterMacroTable = new LookupTable<String, SimpleMacroSymbol>();
    protected final LookupTable<String, SimpleMacroSymbol> exportMacroTable = new LookupTable<String, SimpleMacroSymbol>();
    protected final LookupTable<String, SimpleMacroSymbol> undeclaredMacroTable = new LookupTable<String, SimpleMacroSymbol>();

}
